package org.uams.dbmi.extractSubset;

import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.io.RDFXMLOntologyFormat;
import org.semanticweb.owlapi.model.AddAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLOntologyStorageException;
import org.semanticweb.owlapi.model.OWLSubClassOfAxiom;
import org.semanticweb.owlapi.util.OWLClassExpressionVisitorAdapter;
import org.semanticweb.owlapi.util.OWLEntityRemover;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.protege.editor.owl.model.hierarchy.OWLObjectHierarchyProvider;
import org.protege.editor.owl.ui.view.cls.AbstractOWLClassViewComponent;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;


public class BuildSubset extends AbstractOWLClassViewComponent {

	private JTextArea namesComponent;
	private JButton selectDirectory;
	private JButton extractGo;
	private JFileChooser fileDirectory;
	private JTextField closestTerm;
	private JTextField farthestTerm;
	private String toDirectory ="";
	private String closest;
	private String farthest;
	private OWLClass closestClass;
	private OWLClass farthestClass;
	private OWLClass selectedClass;
	private OWLObjectHierarchyProvider<OWLClass> hp;
	private JRadioButton isClosestSelected;
	private JRadioButton isFarthestSelected;

	// create the GUI
	public void initialiseClassView() throws Exception {
		setLayout(new BorderLayout(6, 6));
		namesComponent = new JTextArea();
		namesComponent.setTabSize(2);

		selectDirectory = new JButton("Select the Directory to store the output OWL files");
		selectDirectory.addActionListener(actionListener);

		closest = "Select the class closest to the root";
		closestTerm = new JTextField(closest);
		closestTerm.setEditable(false);

		isClosestSelected = new JRadioButton("Closest");
		isClosestSelected.addActionListener(closestListener);

		farthest = "Select the class farthest from the root";
		farthestTerm = new JTextField(farthest);
		farthestTerm.setEditable(false);

		isFarthestSelected = new JRadioButton("Farthest");
		isFarthestSelected.addActionListener(farthestListener);

		ButtonGroup btnGroup = new ButtonGroup();
		btnGroup.add(isClosestSelected);
		btnGroup.add(isFarthestSelected);

		extractGo = new JButton("Extract Now!");
		extractGo.setEnabled(false);
		extractGo.addActionListener(extractActionListener);

		fileDirectory = new JFileChooser();
		fileDirectory.setCurrentDirectory(new java.io.File("."));
		fileDirectory.setDialogTitle("Select the Directory to store the output OWL files");
		fileDirectory.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		fileDirectory.setAcceptAllFileFilterUsed(false);

		JPanel inputPanel = new JPanel();
		inputPanel.setLayout(new BorderLayout(6,6));

		inputPanel.add(selectDirectory,BorderLayout.PAGE_START);
		inputPanel.add(extractGo,BorderLayout.PAGE_END);

		JPanel inPanel2 = new JPanel();
		inPanel2.setLayout(new BoxLayout(inPanel2,BoxLayout.PAGE_AXIS));
		inPanel2.add(isClosestSelected);
		inPanel2.add(isFarthestSelected);
		inputPanel.add(inPanel2,BorderLayout.WEST);

		JPanel inPanel3 = new JPanel();
		inPanel3.setLayout(new BoxLayout(inPanel3,BoxLayout.PAGE_AXIS));
		inPanel3.add(closestTerm);
		inPanel3.add(farthestTerm);
		inputPanel.add(inPanel3,BorderLayout.CENTER);

		add(inputPanel,BorderLayout.NORTH);
		add(new JScrollPane(namesComponent), BorderLayout.CENTER);
	}

	private ActionListener actionListener = new ActionListener(){

		public void actionPerformed(ActionEvent actionEvent) {
			namesComponent.setText("");
			if (fileDirectory.showOpenDialog(getParent())==JFileChooser.APPROVE_OPTION)
				toDirectory = fileDirectory.getSelectedFile().toString();   
			namesComponent.append("\n");
			namesComponent.append("The selected Output Directory is "+toDirectory);
			namesComponent.append("\n");
			namesComponent.append("---------------------");
			namesComponent.append("\n");
			if (!farthestTerm.getText().equals(farthest) && !closestTerm.getText().equals(closest) && !toDirectory.equals(""))
				extractGo.setEnabled(true);

			if (extractGo.isSelected()){
				namesComponent.append("\n");
				namesComponent.append("Extraction in Progress...");				
			}
		}
	};

	private ActionListener extractActionListener = new ActionListener(){

		public void actionPerformed(ActionEvent actionEvent) {
			hp = getOWLModelManager().getOWLHierarchyManager().getOWLClassHierarchyProvider();
			render(farthestClass, 0);
			System.out.println("Output Directory "+toDirectory);
			BuildFiles(closestClass,farthestClass,toDirectory);
		}
	};

	private ActionListener closestListener = new ActionListener(){


		public void actionPerformed(ActionEvent actionEvent) {
			closestTerm.setBackground(Color.ORANGE);
			farthestTerm.setBackground(Color.WHITE);			
		}

	};

	private ActionListener farthestListener = new ActionListener(){

		@Override
		public void actionPerformed(ActionEvent actionEvent) {
			closestTerm.setBackground(Color.WHITE);
			farthestTerm.setBackground(Color.ORANGE);

		}

	};

	// called automatically when the global selection changes
	protected OWLClass updateView(OWLClass selectedClass) {
		System.out.println(closestTerm.getText());
		System.out.println(farthestTerm.getText());
		if (!isClosestSelected.isSelected() && !isFarthestSelected.isSelected()){
			isClosestSelected.setSelected(true);
		}
		if (selectedClass != null){
			if (isClosestSelected.isSelected()){
				closestTerm.setText("Closest to root: "+selectedClass.toString());
				closestClass = selectedClass;
			}
			if 	(isFarthestSelected.isSelected()){
				farthestTerm.setText("Farthest from root: "+selectedClass.toString());
				farthestClass = selectedClass;
			}
		}
		System.out.println("To Directory: "+toDirectory);
		if (!farthestTerm.getText().equals(farthest) && !closestTerm.getText().equals(closest) && !toDirectory.equals(""))
			extractGo.setEnabled(true);

		if (extractGo.isSelected()){
			namesComponent.append("\n");
			namesComponent.append("Extraction in Progress...");				
		}
		return selectedClass;
	}

	// render the class and recursively all of its subclasses
	private void render(OWLClass selectedClass, int indent) {
		for (int i=0; i<indent; i++){
			namesComponent.append("\t");
		}
		namesComponent.append(getOWLModelManager().getRendering(selectedClass));
		namesComponent.append("\n");
		// the hierarchy provider gets subclasses for us
		for (OWLClass sub: hp.getParents(selectedClass)){
			render(sub, indent+1);
			if (sub.equals(closestClass)){
				System.out.println("sub: "+sub);
				break;				
			}
		}
	}

	// remove any listeners and perform tidyup (none required in this case)
	public void disposeView() {
		fileDirectory.removeActionListener(actionListener);
	}
	/**
	 * Read RDF XML from a file and write to a compact format
	 */
	private void BuildFiles(OWLClass ClosestClass,OWLClass LowestClass, String toDirectory){

		try {

			ArrayList<IRI> fileNameArray = new ArrayList<IRI>();
			ArrayList<OWLOntologyManager> ontoMan = new ArrayList<OWLOntologyManager>();
			ArrayList<OWLOntology> ontoList = new ArrayList<OWLOntology>();
			namesComponent.append("\n");
			namesComponent.append("---------------------");
			namesComponent.append("\n");
			namesComponent.append("The main ontology file is");
			namesComponent.append("\n");
			namesComponent.append("---------------------");
			namesComponent.append("\n");
			OWLOntology mowo = getOWLModelManager().getActiveOntology();
			fileNameArray.add(mowo.getOntologyID().getOntologyIRI());
			namesComponent.append(fileNameArray.get(0).toString());
			//		System.out.println(mowo.getOntologyID().getOntologyIRI());
			//		System.out.println(mowo.getOntologyID().getOntologyIRI().getFragment());
			//		System.out.println(mowo.getOntologyID().getOntologyIRI().getScheme());
			//		System.out.println(mowo.getOntologyID().getOntologyIRI().getStart());
			namesComponent.append("\n");
			namesComponent.append("The imports are");
			namesComponent.append("\n");
			namesComponent.append("---------------------");
			namesComponent.append("\n");
			for(OWLOntology importedOntology : mowo.getImports()) {
				fileNameArray.add(importedOntology.getOntologyID().getOntologyIRI());
				namesComponent.append(importedOntology.getOntologyID().getOntologyIRI().toString());
				namesComponent.append("\n");
				//			System.out.println(importedOntology.getOntologyID().getOntologyIRI());
				//			System.out.println(importedOntology.getOntologyID().getOntologyIRI().getFragment());
				//			System.out.println(importedOntology.getOntologyID().getOntologyIRI().getScheme());
				//			System.out.println(importedOntology.getOntologyID().getOntologyIRI().getStart());
			}
			DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
			Date date = new Date();
			System.out.println(dateFormat.format(date));

			for(int i=0; i < fileNameArray.size() ; i++)  
				ontoMan.add(OWLManager.createOWLOntologyManager());
			for (int i=0; i<ontoMan.size(); i++){
				System.out.println(fileNameArray.get(i));
				ontoList.add(ontoMan.get(i).loadOntology(fileNameArray.get(i)));
			}
			System.out.println("IRI for LowestClass: "+LowestClass.getIRI());

			SuperClassesVisitor SCVisitor = new SuperClassesVisitor(ontoList);

			LowestClass.accept(SCVisitor);
			System.out.println("Class Hierarchy for " + LowestClass + ": "
					+ SCVisitor.getSuperClasses().size());

			TreeSet<String> termsToKeep = new TreeSet<String>();
			boolean isValidSet = false;
			for (OWLClass prop : SCVisitor.getSuperClasses()) {
				System.out.println("    " + prop);
				termsToKeep.add(prop.toString());
				if (prop.equals(closestClass)){
					isValidSet=true;
					break;
				}
			}
			if (isValidSet==false){
				namesComponent.append("\n");
				namesComponent.append("Invalid selection:\n check the classes to ensure that class selected as closest to the root \n");
				namesComponent.append(closestClass.getIRI().getFragment()+"\n");
				namesComponent.append("is an ancestor node for the class selected as the farthest class \n");								
				namesComponent.append(farthestClass.getIRI().getFragment()+"\n");
				namesComponent.append("Extraction process aborted");
			} 
			else {

				boolean[] changed = new boolean[ontoList.size()];
				for (int i = 0; i < ontoList.size(); i++){

					OWLOntologyManager om = ontoMan.get(i);
					OWLOntology owo = ontoList.get(i);
					OWLDataFactory factory = om.getOWLDataFactory();
					SuperClassVisitor SClVisitor = new SuperClassVisitor(Collections.singleton(owo));

					OWLEntityRemover remover = new OWLEntityRemover(om,Collections.singleton(owo));
					LinkedList<AddAxiom> addAxList = new LinkedList<AddAxiom>();

					for (OWLClass classT : owo.getClassesInSignature()) {
						System.out.println(classT.toString()+ "  "+ classT.getSubClasses(owo).size() + " "+ classT.getSuperClasses(owo).size());
						if (!termsToKeep.contains(classT.toString())) {

							Iterator<OWLClassExpression> subs = classT
							.getSubClasses(owo).iterator();
							Iterator<OWLClassExpression> suppers = classT
							.getSuperClasses(owo).iterator();
							if (suppers.hasNext()){
								while (suppers.hasNext()) {
									OWLClassExpression superCls = suppers.next();
									//								System.out.println("Super Class to "+ classT.getIRI().toString()+": "
									//										+ superCls);
									superCls.accept(SClVisitor);
									if (SClVisitor.getSuperClass().size() == 1) {
										OWLClass superClass = SClVisitor.getSuperClass().get(0);
										//									System.out.println("SuperClass - ClassT: "+ superClass.toString());
										SClVisitor.reset();
										while (subs.hasNext()) {
											OWLClass sub = subs.next().asOWLClass();
											//										System.out.println("SubClass to "+classT.getIRI()+": "+ sub);
											OWLAxiom ax = factory.getOWLSubClassOfAxiom(sub,superClass);
											AddAxiom addAx = new AddAxiom(owo, ax);
											addAxList.add(addAx);
										}
									}
									om.applyChanges(addAxList);
									addAxList.clear();
									classT.accept(remover);
									System.out.println("Deleting "+classT.getIRI());
									changed[i] = true;
									om.applyChanges(remover.getChanges());
									remover.reset();
									break;
								}
							}else {
								classT.accept(remover);
								System.out.println("Deleting "+classT.getIRI());
								changed[i] = true;
								om.applyChanges(remover.getChanges());
								remover.reset();
							}
						}
					}
				}
				namesComponent.append("\n");
				namesComponent.append("\n"+closestTerm.getText());
				namesComponent.append("\n"+farthestTerm.getText()+"\n\n");
				namesComponent.append("---------------------");
				namesComponent.append("\n");
				namesComponent.append("The Output files are in "+toDirectory);
				namesComponent.append("\n");
				namesComponent.append("---------------------");
				namesComponent.append("\n");
				for (int i1=0;i1<ontoMan.size();i1++){
					// Save to RDF/XML
					System.out.println("Save file " + toDirectory+"/"+fileNameArray.get(i1).getFragment()+ " "+dateFormat.format(date));
					ontoMan.get(i1).saveOntology(ontoList.get(i1), new RDFXMLOntologyFormat(),IRI.create("file:" + toDirectory+"/"+ fileNameArray.get(i1).getFragment()));
					String ch = (changed[i1]?"changed":"Unchanged");
					namesComponent.append(ch+" "+toDirectory+"/"+fileNameArray.get(i1).getFragment());
					namesComponent.append("\n");
				}
				namesComponent.append("Extraction Process Completed");
			}
			closestTerm.setText(closest);
			farthestTerm.setText(farthest);
			extractGo.setEnabled(false);

		} catch (OWLOntologyCreationException e) {
			System.out.println("Coult not load ontology: " + e.getMessage());
		} catch (OWLOntologyStorageException e) {
			System.out.println("Problem saving ontology: " + e.getMessage());
		}
	}
	private static class SuperClassesVisitor extends	OWLClassExpressionVisitorAdapter {

		private boolean processInherited = true;
		private List<OWLClass> processedClasses;
		private ArrayList<OWLOntology> onts;

		public SuperClassesVisitor(ArrayList<OWLOntology> onts) {
			processedClasses = new ArrayList<OWLClass>();
			this.onts = onts;
		}

		public List<OWLClass> getSuperClasses() {
			return processedClasses;
		}

		public void visit(OWLClass desc) {

			System.out.println("Visit...."+desc);
			if (processInherited && !processedClasses.contains(desc)) {
				processedClasses.add(desc);
				for (OWLOntology ont : onts) {
					for (OWLSubClassOfAxiom ax : ont.getSubClassAxiomsForSubClass(desc)) {
						//						System.out.println("ax: "+ax);
						ax.getSuperClass().accept(this);
					}
				}
			}
		}

	}

	private static class SuperClassVisitor extends OWLClassExpressionVisitorAdapter {

		private boolean processInherited = true;
		private List<OWLClass> processedClasses;
		private Set<OWLOntology> onts;

		public SuperClassVisitor(Set<OWLOntology> onts) {
			processedClasses = new ArrayList<OWLClass>();
			this.onts = onts;
		}

		public List<OWLClass> getSuperClass() {
			return processedClasses;
		}

		public void visit(OWLClass desc) {
			if (processInherited && !processedClasses.contains(desc)) {
				processedClasses.add(desc);
			}
		}
		public void reset() {
			processedClasses.clear();
		}		

	}

}
